package com.unicloud.medical.oss.client;

import com.unicloud.medical.oss.client.properties.OSSClientProperties;
import com.unicloud.medical.oss.client.util.RestUtil;
import com.unicloud.medical.oss.common.constant.CommonConstant;
import com.unicloud.medical.oss.common.constant.EndpointConstant;
import com.unicloud.medical.oss.common.enums.ErrorEnum;
import com.unicloud.medical.oss.common.model.BucketInfo;
import com.unicloud.medical.oss.common.model.DownloadObject;
import com.unicloud.medical.oss.common.model.FileMetaData;
import com.unicloud.medical.oss.common.model.ObjectInfo;
import com.unicloud.medical.oss.common.model.request.*;
import com.unicloud.medical.oss.core.OSSServiceFinder;
import com.unisound.medical.common.model.dto.ResultDTO;
import com.unisound.medical.common.util.AssertUtil;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.util.*;

@Slf4j
public class HttpOSSClient extends BaseOSSClient {

    public HttpOSSClient(OSSClientProperties ossClientProperties, OSSServiceFinder ossServiceFinder) {
        super(ossClientProperties, ossServiceFinder);
    }

    /**
     * 查看存储桶是否存在
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "bucketExistFallback")
    @Override
    public ResultDTO<Boolean> doBucketExist(BucketExistRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.BUCKET_EXIST;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<Boolean>>() {});
    }

    /**
     * 创建存储桶
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "createBucketFallback")
    @Override
    public ResultDTO<Boolean> doCreateBucket(CreateBucketRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CREATE_BUCKET;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<Boolean>>() {});
    }

    /**
     * 创建默认权限存储桶(默认策略支持浏览器直接访问url)
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "createDefaultPolicyBucketFallback")
    @Override
    public ResultDTO<Boolean> doCreateDefaultPolicyBucket(CreateBucketRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CREATE_DEFAULT_POLICY_BUCKET;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<Boolean>>() {});
    }

    /**
     * 删除存储桶
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "removeBucketFallback")
    @Override
    public ResultDTO<Boolean> doRemoveBucket(DeleteBucketRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.REMOVE_BUCKET;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<Boolean>>() {});
    }

    /**
     * 获取全部存储桶
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "getAllBucketsFallback")
    @Override
    public ResultDTO<List<BucketInfo>> doGetAllBuckets(CommonRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.ALL_BUCKET;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<BucketInfo>>>() {});
    }

    /**
     * 获取当前存储桶所有文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "listAllObjectsCurrBucketFallback")
    @Override
    public ResultDTO<List<ObjectInfo>> doListAllObjectsCurrBucket(ListAllObjectsCurrBucketRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CURR_BUCKET_ALL_OBJECT;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<ObjectInfo>>>() {});
    }

    /**
     * 获取当前存储桶当前文件夹所有文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "listObjectsCurrBucketAndFolderFallback")
    @Override
    public ResultDTO<List<ObjectInfo>> doListObjectsCurrBucketAndFolder(ListAllObjectsCurrBucketAndFolderRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CURR_BUCKET_AND_FOLDER_ALL_OBJECT;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<ObjectInfo>>>() {});
    }

    /**
     * 文件上传
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "uploadObjectsFallback")
    @Override
    public ResultDTO<List<String>> doUploadObjects(InnerUploadObjectsRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.UPLOAD_OBJECTS;
        return RestUtil.postFile(url, uploadObjectsRequest(request), new ParameterizedTypeReference<ResultDTO<List<String>>>() {});
    }

    private MultiValueMap<String, Object> uploadObjectsRequest(InnerUploadObjectsRequest request) {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap();
        map.add("ossName", request.getOssName());
        map.add("bucketName", request.getBucketName());
        map.add("folder", request.getFolder());

        List<ByteArrayResource> resources = buildResources(request.getFiles());
        AssertUtil.assertTrue(CollectionUtils.isNotEmpty(resources), ErrorEnum.UPLOAD_FILES_FAIL);
        for (ByteArrayResource resource : resources) {
            map.add("files", resource);
        }
        return map;
    }

    private ByteArrayResource buildResource(FileMetaData file) {
        try {
            return new ByteArrayResource(file.getData()) {
                @Override
                public String getFilename() {
                    return file.getFileName();
                }
            };
        } catch (Exception e) {
            return null;
        }
    }

    private List<ByteArrayResource> buildResources(List<FileMetaData> files) {
        List<ByteArrayResource> resources = new ArrayList<>();
        for (FileMetaData file : files) {
            ByteArrayResource resource = buildResource(file);
            if (Objects.nonNull(resource)) {
                resources.add(resource);
            }
        }
        return resources;
    }

    private ByteArrayResource buildLocalResource(String fileName) {
        try {
            File file = new File(fileName);
            if (file == null || !file.exists() || file.isDirectory()) return null;

            return new ByteArrayResource(FileUtils.readFileToByteArray(file)) {
                @Override
                public String getFilename() {
                    return file.getName();
                }
            };
        } catch (Exception e) {
            return null;
        }
    }

    private List<ByteArrayResource> buildLocalResources(List<String> fileNames) {
        try {
            List<ByteArrayResource> resources = new ArrayList<>();
            for (String fileName : fileNames) {
                ByteArrayResource resource = buildLocalResource(fileName);
                if (Objects.nonNull(resource)) {
                    resources.add(resource);
                }
            }
            return resources;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 文件上传
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "uploadObjectFallback")
    @Override
    public ResultDTO<String> doUploadObject(InnerUploadObjectRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.UPLOAD_OBJECT;
        return RestUtil.postFile(url, uploadObjectRequest(request), new ParameterizedTypeReference<ResultDTO<String>>() {});
    }

    private MultiValueMap<String, Object> uploadObjectRequest(InnerUploadObjectRequest request) {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap();
        map.add("ossName", request.getOssName());
        map.add("bucketName", request.getBucketName());
        map.add("objectName", request.getObjectName());

        ByteArrayResource resource = buildResource(request.getFile());
        AssertUtil.assertTrue(Objects.nonNull(resource), ErrorEnum.UPLOAD_FILES_FAIL);
        map.add("file", resource);
        return map;
    }

    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "uploadLocalObjectFallback")
    public ResultDTO<String> doUploadLocalObject(UploadLocalObjectRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.UPLOAD_OBJECT;
        return RestUtil.postFile(url, uploadLocalObjectRequestToMap(request), new ParameterizedTypeReference<ResultDTO<String>>() {});
    }

    private MultiValueMap<String, Object> uploadLocalObjectRequestToMap(UploadLocalObjectRequest request) {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap();
        map.add("ossName", request.getOssName());
        map.add("bucketName", request.getBucketName());
        map.add("objectName", request.getObjectName());

        ByteArrayResource resource = buildLocalResource(request.getFileName());
        AssertUtil.assertTrue(Objects.nonNull(resource), ErrorEnum.UPLOAD_FILES_FAIL);
        map.add("file", resource);
        return map;
    }

    /**
     * 批量本地文件上传
     */
    @Override
    public ResultDTO<List<String>> doUploadLocalObjects(UploadLocalObjectsRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.UPLOAD_OBJECTS;
        return RestUtil.postFile(url, uploadLocalObjectRequestToMap(request), new ParameterizedTypeReference<ResultDTO<List<String>>>() {});
    }

    private MultiValueMap<String, Object> uploadLocalObjectRequestToMap(UploadLocalObjectsRequest request) {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap();
        map.add("ossName", request.getOssName());
        map.add("bucketName", request.getBucketName());
        map.add("folder", request.getFolder());

        List<ByteArrayResource> resources = buildLocalResources(request.getFileNames());
        AssertUtil.assertTrue(CollectionUtils.isNotEmpty(resources), ErrorEnum.UPLOAD_FILES_FAIL);
        for (ByteArrayResource resource : resources) {
            map.add("files", resource);
        }
        return map;
    }

    /**
     * 文件下载
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "downloadObjectFallback")
    @Override
    public ResultDTO<DownloadObject> doDownloadObject(DownloadObjectRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.DOWNLOAD_OBJECT;
        Map<String, String> requestMap = downloadObjectRequestToMap(request);
        return RestUtil.post(url, requestMap, new ParameterizedTypeReference<ResultDTO<DownloadObject>>() {});
    }

    private Map<String, String> downloadObjectRequestToMap(DownloadObjectRequest request) {
        Map<String, String> requestMap = new HashMap<>();
        requestMap.put("ossName", request.getOssName());
        requestMap.put("bucketName", request.getBucketName());
        requestMap.put("objectName", request.getObjectName());
        return requestMap;
    }

    /**
     * 下载文件夹下所有文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "downloadObjectsFallback")
    @Override
    public ResultDTO<DownloadObject> doDownloadObjects(DownloadObjectsRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CURR_FOLDER_DOWNLOAD_OBJECTS;
        Map<String, String> requestMap = downloadObjectsRequestToMap(request);
        RestUtil.getForEntity(url, requestMap);
        return null;
    }

    private Map<String, String> downloadObjectsRequestToMap(DownloadObjectsRequest request) {
        Map<String, String> requestMap = new HashMap<>();
        requestMap.put("ossName", request.getOssName());
        requestMap.put("bucketName", request.getBucketName());
        requestMap.put("folder", request.getFolder());
        requestMap.put("fileName", request.getFileName());
        return requestMap;
    }

    /**
     * 根据url地址删除文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "removeObjectFallback")
    @Override
    public ResultDTO<String> doRemoveObject(RemoveObjectRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.DELETE_OBJECT;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<String>>() {});
    }

    /**
     * 根据url地址列表批量删除文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "removeObjectsFallback")
    @Override
    public ResultDTO<List<String>> doRemoveObjects(RemoveObjectsRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.DELETE_OBJECTS;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<String>>>() {});
    }

    /**
     * 删除当前桶当前文件夹所有文件
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "removeObjectsCurrBucketAndFolderFallback")
    @Override
    public ResultDTO<List<String>> doRemoveObjects(RemoveObjectsCurrBucketAndFolderRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.CURR_FOLDER_DELETE_OBJECTS;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<String>>>() {});
    }

    /**
     * 文件复制
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "copyObjectFallback")
    @Override
    public ResultDTO<String> doCopyObject(CopyObjectRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.COPY_OBJECT;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<String>>() {});
    }

    /**
     * 已签名地址
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "signedUrlFallback")
    @Override
    public ResultDTO<String> doSignedUrl(SignedUrlRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.SIGNED_URL;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<String>>() {});
    }

    /**
     * 批量已签名地址
     */
    @CircuitBreaker(name = CommonConstant.DEFAULT_CIRCUIT_BREAKER_NAME, fallbackMethod = "signedUrlsFallback")
    @Override
    public ResultDTO<List<String>> doSignedUrls(SignedUrlsRequest request) {
        String url = getOssClientProperties().getEndpoint() + EndpointConstant.OSS_V1 + EndpointConstant.SIGNED_URLS;
        return RestUtil.post(url, request, new ParameterizedTypeReference<ResultDTO<List<String>>>() {});
    }

}
